﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.SmallBasic.Library;
using mshtml;
using SHDocVw;

namespace IE4SmallBasic
{
#if LANG_JA
    /// <summary>
    /// IEオブジェクトは、Internet Exploer を使って Web サイトにアクセスする機能を提供します。
    /// </summary>
#else
    /// <summary>
    /// The IE object provides methods to access any Web sites using by Internet Explorer.
    /// </summary>
#endif
    [SmallBasicType]
    public static class IE
    {
        private static InternetExplorer _ie;

        private static InternetExplorer ie
        {
            get
            {
                if (_ie == null)
                {
                    _ie = new InternetExplorerClass();
                    _ie.Visible = true;
                }
                return _ie;
            }
        }

        private static void WaitForComplete()
        {
            while (ie.ReadyState != tagREADYSTATE.READYSTATE_COMPLETE)
            {
                Thread.Sleep(200);
            }
        }

#if LANG_JA
        /// <summary>
        /// 指定した URL の Web サイトを開きます。
        /// </summary>
        /// <param name="url">開く Web サイトの URL です。(例 "http://smallbasic.com")</param>
#else
        /// <summary>
        /// Open the web site of specified URL.
        /// </summary>
        /// <param name="url">The URL of web site to open. (ex. "http://smallbasic.com")</param>
#endif
        public static void Navigate(Primitive url)
        {
            ie.Navigate(url);
            WaitForComplete();
        }

        private static void ApplyActionToElement(string elementId, Action<IHTMLElement> action)
        {
            ApplyFuncToElement(elementId, targetElement =>
            {
                action(targetElement);
                return default(object);
            });
        }

        private static T ApplyFuncToElement<T>(string elementId, Func<IHTMLElement, T> action)
        {
            var doc = ie.Document as IHTMLDocument3;
            var targetElement = default(IHTMLElement);
            for (int i = 0; i < 5 * 1000 / 200 && targetElement == null; Thread.Sleep(200), i++)
            {
                targetElement = doc.getElementById(elementId);
            }

            var result = action(targetElement);

            Marshal.ReleaseComObject(targetElement);
            Marshal.ReleaseComObject(doc);
            WaitForComplete();

            return result;
        }

#if LANG_JA
        /// <summary>
        /// 現在表示されている Web ページ中の、指定した ID の INPUT 要素に、指定した値を設定します。
        /// </summary>
        /// <param name="elementId">値を設定する INPUT 要素の ID です。</param>
        /// <param name="newValue">指定した INPUT 要素に設定する値です。</param>
#else
        /// <summary>
        /// Set new value to the input element specified by ID in current web page.
        /// </summary>
        /// <param name="elementId">The ID of input element to set new value.</param>
        /// <param name="newValue">The new value to set at the specified input element.</param>
#endif
        public static void SetValue(Primitive elementId, Primitive newValue)
        {
            ApplyActionToElement(elementId, targetElement =>
            {
                targetElement.setAttribute("value", newValue.ToString());
            });
        }

#if LANG_JA
        /// <summary>
        /// 現在表示されている Web ページ中の、指定した ID の要素をクリックします。
        /// </summary>
        /// <param name="elementId">クリックする要素の ID です。</param>
#else
        /// <summary>
        /// Click the element specified by ID in current web page.
        /// </summary>
        /// <param name="elementId">The ID of element to click.</param>
#endif
        public static void Click(Primitive elementId)
        {
            ApplyActionToElement(elementId, targetElement =>
            {
                targetElement.click();
            });
        }

#if LANG_JA
        /// <summary>
        /// 現在表示されている Web ページ中の、指定した ID の INPUT 要素に入力されている内容を読み取ります。
        /// </summary>
        /// <param name="elementId">値を読み取る対象の INPUT 要素の ID です。</param>
        /// <returns>指定した ID の INPUT 要素から読み取った入力内容です。</returns>
#else
        /// <summary>
        /// Get entried value from the input element specified by ID in current web page.
        /// </summary>
        /// <param name="elementId">The ID of input element to be get entried value.</param>
        /// <returns>The value of the specified input element.</returns>
#endif
        public static Primitive GetValue(Primitive elementId)
        {
            return ApplyFuncToElement(elementId, targetElement =>
            {
                return targetElement.getAttribute("value").ToString(); ;
            });
        }

#if LANG_JA
        /// <summary>
        /// 現在表示されている Web ページ中の、指定した ID の要素中のテキストを読み取ります。
        /// </summary>
        /// <param name="elementId">テキストを読み取る要素の ID です。</param>
        /// <returns>指定した ID の要素から読み取ったテキストです。</returns>
#else
        /// <summary>
        /// Get inner text from the element specified by ID in current web page.
        /// </summary>
        /// <param name="elementId">The ID of element to be get inner text.</param>
        /// <returns>The inner text of specified element.</returns>
#endif
        public static Primitive GetText(Primitive elementId)
        {
            return ApplyFuncToElement(elementId, targetElement =>
            {
                return targetElement.innerText;
            });
        }

#if LANG_JA
        /// <summary>
        /// 現在表示されている Web ページを、通常使うプリンターに印刷します。
        /// </summary>
#else
        /// <summary>
        /// Print out current web page to default printer.
        /// </summary>
#endif
        public static void Print()
        {
            ie.ExecWB(OLECMDID.OLECMDID_PRINT, OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER);
        }

    }
}
